package com.developteam.xnmall.service.impl;

import com.developteam.xnmall.dao.OrderItemMapper;
import com.developteam.xnmall.dao.OrdersMapper;
import com.developteam.xnmall.dao.ProductSkuMapper;
import com.developteam.xnmall.dao.ShoppingCartMapper;
import com.developteam.xnmall.entity.*;
import com.developteam.xnmall.service.OrderService;
import com.developteam.xnmall.utils.PageHelper;
import com.developteam.xnmall.vo.ResStatus;
import com.developteam.xnmall.vo.ResultVO;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;
    @Autowired
    private ProductSkuMapper productSkuMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    private Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);

    /**
     * 保存订单业务
     */
    @Transactional
    public Map<String, String> addOrder(String cids, Orders order) {  ///购物车记录的id，订单
        logger.info("add order again");
        HashMap<String, String> map = new HashMap<>();

        //1.校验库存：根据cids查询当前订单中关联的购物⻋记录详情（包括库存）
        String[] arr = cids.split(",");
        ArrayList<Integer> cidsList = new ArrayList<>();
        for (int i = 0; i < arr.length; i++) {
            cidsList.add(Integer.parseInt(arr[i]));
        }
        //根据⽤户在购物⻋列表中选择的购物⻋记录的id 查询到对应的购物⻋记录
        List<ShoppingCartVO> list = shoppingCartMapper.selectShopcartByCids(cidsList);
        boolean isLock = true;
        String[] skuIds = new String[list.size()];  ///每件商品sku的id
        Map<String, RLock> locks = new HashMap<>();  //⽤于存放当前订单的锁
        for (int i = 0; i < list.size(); i++) {
            String skuId = list.get(i).getSkuId();  //从购物车记录ShoppingCartVO中拿到
            boolean b = false;
            try {
                RLock lock = redissonClient.getLock(skuId);
                b = lock.tryLock(3,20, TimeUnit.SECONDS);
                if (b) {
                    skuIds[i] = skuId;
                    locks.put(skuId, lock);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            isLock = isLock & b;
        }
        try {
            if (isLock) {
                boolean f = true;
                String untitled = "";
                //1.检验库存
                list = shoppingCartMapper.selectShopcartByCids(cidsList);
                for (ShoppingCartVO sc : list) {
                    if (Integer.parseInt(sc.getCartNum()) > sc.getSkuStock()) {  //让订单的商品数量和库存数量在一张表上
                        f = false;
                    }
                    untitled = untitled + sc.getProductName() + ",";
                }
                if (f) {
                    //如果库存充⾜，则进⾏下订单操作
                    logger.info("product stock is OK....");

                    //2.保存订单
                    order.setUntitled(untitled);
                    order.setCreateTime(new Date());
                    order.setStatus("1");
                    //⽣成订单编号
                    String orderId = UUID.randomUUID().toString().replace("-", "");
                    order.setOrderId(orderId);
                    int i = ordersMapper.insert(order);

                    //3.⽣成商品快照
                    for (ShoppingCartVO sc : list) {
                        int cnum = Integer.parseInt(sc.getCartNum());
                        String itemId = System.currentTimeMillis() + "" + (new Random().nextInt(89999) + 10000);
                        OrderItem orderItem = new OrderItem(itemId, orderId, sc.getProductId(), sc.getProductName(), sc.getProductImg(), sc.getSkuId(), sc.getSkuName(), new BigDecimal(sc.getSellPrice()), cnum, new BigDecimal(sc.getSellPrice() * cnum), new Date(), new Date(), 0);
                        orderItemMapper.insert(orderItem);
                    }

                    //4.扣减库存：根据套餐ID修改套餐库存量
                    for (ShoppingCartVO sc : list) {
                        String skuId = sc.getSkuId();
                        int newStock = sc.getSkuStock() - Integer.parseInt(sc.getCartNum());
                        ProductSku productSku = new ProductSku();
                        productSku.setSkuId(skuId);
                        productSku.setStock(newStock);
                        productSkuMapper.updateByPrimaryKeySelective(productSku);  //更新不为空的字段，updateByPrimaryKey后者则会将为空的字段在数据库中置为NULL
                    }

                    //5.删除购物⻋：当购物⻋中的记录购买成功之后，购物⻋中对应做删除操作
                    for (int cid : cidsList) {
                        shoppingCartMapper.deleteByPrimaryKey(cid);
                    }
                    map = new HashMap<>();
                    logger.info("add order finished");
                    map.put("orderId", orderId);
                    map.put("productNames", untitled);
                }
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } finally {
//            一定会释放锁
            for (int i = 0; i < skuIds.length; i++) {
                String skuId = skuIds[i];
                if (skuId != null && !"".equals(skuId)) {
                    locks.get(skuId).unlock();
                    System.out.println("-------------------unlock");
                }
            }
        }
        return map;
    }
//    @Transactional
//    public Map<String, String> addOrder(String cids, Orders order) throws SQLException {
//        logger.info("add order begin...");
//        Map<String, String> map = new HashMap<>();
//
////        都是操作数据库的事务，要么同时成功，要么一起失败，（如果快照生成失败，则订单也无，库存也不会改变），要用到spring的事务管理@Transactional即可
////        第一步做完也不会提交，而是等方法中其他步骤也做完，一直等到最后一步return后全部提交，但凡有一步操作失败，整个数据库事务就会回滚，所有操作均不会被提交
//
//        //1.校验库存：根据cids查询当前订单中关联的购物车记录详情（包括库存）  、
//        ///一个订单中购买多个商品，一个商品库存就不行
//        String[] arr = cids.split(",");
//        List<Integer> cidsList = new ArrayList<>();
//        for (int i = 0; i < arr.length; i++) {
//            cidsList.add(Integer.parseInt(arr[i]));
//        }
////        根据购物车id获取到购物车记录
//        List<ShoppingCartVO> list = shoppingCartMapper.selectShopcartByCids(cidsList);
////        从购物车信息中获取到要购买的skuid（商品id），以skuid为key写到redis中
//        boolean isLock = true;
////        要记录对哪个商品加锁成功
//        String[] skuIds = new String[list.size()];
//        Map<String, RLock> locks = new HashMap<>();   ///{"1" : lock1; null}
////        HashMap<String, String> values = new HashMap<>();
//        for (int i = 0; i < list.size(); i++) {
//            String skuId = list.get(i).getSkuId();
////            //为当前商品加锁的时候，生成唯一的value
////            String value = UUID.randomUUID().toString();
////            //添加订单之前，对订单的每个 skuid都锁定，sku的表里面有库存，
////            //线程出现故障导致无法正常退出释放锁，延迟 10s也能自动清除
//////            Boolean isAbsent = stringRedisTemplate.boundValueOps(skuId).setIfAbsent("xnmall", 10, TimeUnit.SECONDS);
////            Boolean isAbsent = stringRedisTemplate.boundValueOps(skuId).setIfAbsent(value, 10, TimeUnit.SECONDS);
////
////            if (isAbsent) {
////                skuIds[i] = skuId;
////                values.put(skuId, value);  //【101，唯一值1； 102，唯一值2】
////            }
////            isLock = isLock && isAbsent;
//            //构建当前商品的锁
//            boolean b = false; //拿到锁就要锁住
//            try {
//                RLock lock = redissonClient.getLock(skuId); //多个商品分开锁住 ,拿着锁中的skuid为key，往redis中添加数据
//                b = lock.tryLock(10, 3, TimeUnit.SECONDS);
//                ///因为要释放，所以加锁成功的要记录下来
//                if (b) {
//                    skuIds[i] = skuId;
//                    locks.put(skuId, lock);
//                }
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            isLock = isLock & b;  ///拿到所有商品的锁，才购买 —————— 只不过Redisson帮忙做了设置key-value，启动看门狗线程
//        }
//
//        if (isLock) {    //拿到所有商品的锁了，执行购买业务
//            list = shoppingCartMapper.selectShopcartByCids(cidsList);  //再查一遍，防止修改
//
//            boolean f = true;
//            String untitled = "";
//            for (ShoppingCartVO sc : list) {
//                if (Integer.parseInt(sc.getCartNum()) > sc.getSkuStock()) {
//                    f = false;
//                }
//                //获取所有商品名称，以,分割拼接成字符串
//                untitled = untitled + sc.getProductName() + "" + sc.getSkuName() + "";
//            }
//
//            if (f) {
////            System.out.println("-----库存校验完成");
//                logger.info("product stock is OK...");
//                //2.保存订单
//                order.setUntitled(untitled);
//                order.setCreateTime(new Date());
//                order.setStatus("1");
//                //生成订单编号
//                String orderId = UUID.randomUUID().toString().replace("-", "");
//                order.setOrderId(orderId);
//                int i = ordersMapper.insert(order);
//
//                //3.生成商品快照
////            List<OrderItem> orderItems = new ArrayList<>();
//                for (ShoppingCartVO sc : list) {
//                    int cnum = Integer.parseInt(sc.getCartNum());
//                    String itemId = System.currentTimeMillis() + "" + (new Random().nextInt(89999) + 10000);
//                    OrderItem orderItem = new OrderItem(itemId, orderId, sc.getProductId(), sc.getProductName(), sc.getProductImg(), sc.getSkuId(), sc.getSkuName(), new BigDecimal(sc.getSellPrice()), cnum, new BigDecimal(sc.getSellPrice() * cnum), new Date(), new Date(), 0);
////                orderItems.add(orderItem);
//                    orderItemMapper.insert(orderItem);   //一个一个保存，用list有问题
//                    //增加商品销量
//                }
////            int i = ordersMapper.insertList(orderItems);
//
//                //4.扣减库存：根据套餐ID修改套餐库存量
//                for (ShoppingCartVO sc : list) {
//                    String skuId = sc.getSkuId();
//                    int newStock = sc.getSkuStock() - Integer.parseInt(sc.getCartNum());
//
//                    ProductSku productSku = new ProductSku();
//                    productSku.setSkuId(skuId);
//                    productSku.setStock(newStock);
//                    productSkuMapper.updateByPrimaryKeySelective(productSku);
//                }
//
//                //5.删除购物车：当购物车中的记录购买成功之后，购物车中对应做删除操作
//                for (int cid : cidsList) {
//                    shoppingCartMapper.deleteByPrimaryKey(cid);
//                }
//
//                //购买完成释放锁，购买成功也要释放锁定的那部分
////从redis查询到的skuid对应的value，看失是不是和自己map中加的一样，如果不一样，锁都不是自己加的，就不要释放,解决了过期释放锁的问题
//                for (int m = 0; m < skuIds.length; m++) {
//                    String skuId = skuIds[m];
//                    if (skuId != null && !"".equals(skuId)) {
////                        String s = stringRedisTemplate.boundValueOps(skuId).get();
////                        if (s != null && s.equals(values.get(skuId))) {
//////判断通过还没来得及删除时，redis过期了，别人加锁成功了，依旧是并发带来的问题，很小概率（千万级并发，秒杀），要建立查询比较并删除的原子操作lua脚本
////                            stringRedisTemplate.delete(skuId);
//                        locks.get(skuId).unlock();
//                    }
//                }
//                logger.info("add order finished...");
//                map.put("orderId", orderId);
//                map.put("productNames", untitled);
//                return map;
//            } else {
//                //表示库存不足
//                return null;
//            }
//        } else {
//            //表示对所有skuid加锁失败，订单添加失败
//            //当加锁失败时，可能其他线程对部分商品进行了锁定，所以要释放锁定的部分商品，必须把商品都锁住才能购买【哲学家拿筷子】
//            for (int i = 0; i < skuIds.length; i++) {
//                String skuId = skuIds[i];
//                if (skuId != null && !"".equals(skuId)) {
////                    stringRedisTemplate.delete(skuId);
//                    locks.get(skuId).unlock();  //一、没有给全部上锁就要释放锁， 二、订单添加完成后释放锁
//                }
//            }
//            return null;
//        }
//    }

    @Override
    public int updateOrderStatus(String orderId, String status) {
        Orders orders = new Orders();
        orders.setOrderId(orderId);
        orders.setStatus(status);
        int i = ordersMapper.updateByPrimaryKeySelective(orders);
        return i;
    }

    @Override
    public ResultVO getOrderById(String orderId) {
        Orders order = ordersMapper.selectByPrimaryKey(orderId);
        return new ResultVO(ResStatus.OK, "sucesss", order.getStatus());
    }

    @Override
    @Transactional(isolation = Isolation.SERIALIZABLE)   //事务：还原库存失败，状态也依旧为1待付款，表示使用事务锁
//    当两个用户还原同一个商品的时候，（并发），1.加锁 ； 2.设置隔离级别 SERIALIZABLE，，一个要等着另一个做完
    public void closeOrder(String orderId) {
        synchronized (this) {  //this指OrderServiceImpl对象，让它串行化起来，表示使用JVM锁
            //  1.修改当前订单：status=6 已关闭  close_type=1 超时未支付
            Orders cancleOrder = new Orders();
            cancleOrder.setOrderId(orderId);
            cancleOrder.setStatus("6");  //已关闭
            cancleOrder.setCloseType(1); //关闭类型：超时未支付
            ordersMapper.updateByPrimaryKeySelective(cancleOrder);

            //  2.还原库存：先根据当前订单编号查询商品快照（skuid  buy_count）--->修改product_sku
            Example example1 = new Example(OrderItem.class);
            Example.Criteria criteria1 = example1.createCriteria();
            criteria1.andEqualTo("orderId", orderId);
            List<OrderItem> orderItems = orderItemMapper.selectByExample(example1);
            //还原库存
            for (int j = 0; j < orderItems.size(); j++) {
                OrderItem orderItem = orderItems.get(j);
                //修改
                ProductSku productSku = productSkuMapper.selectByPrimaryKey(orderItem.getSkuId());
                productSku.setStock(productSku.getStock() + orderItem.getBuyCounts());
                productSkuMapper.updateByPrimaryKey(productSku);
            }
        }
    }

    @Override
    public ResultVO listOrders(String userId, String status, int pageNum, int limit) {
        //1.分页查询
        int start = (pageNum - 1) * limit;
        List<OrdersVO> ordersVOS = ordersMapper.selectOrders(userId, status, start, limit);

        //2.查询总记录数——————以下都是查询分页
        Example example = new Example(Orders.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andLike("userId", userId);
        if (status != null && !"".equals(status)) {   //两种空都不行，，有状态就走if查状态
            criteria.andLike("status", status);
        }
        int count = ordersMapper.selectCountByExample(example);

        //3.计算总页数
        int pageCount = count % limit == 0 ? count / limit : count / limit + 1;

        //4.封装数据
        PageHelper<OrdersVO> pageHelper = new PageHelper<>(count, pageCount, ordersVOS);
        return new ResultVO(ResStatus.OK, "SUCCESS", pageHelper);
    }


}
